﻿using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using OpenPop.Pop3;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using PMC.Scripts.Default;
using PMC.Plugins;

namespace PMC.Urls.Default
{
    [ScriptInfo("POP3 EmailFetcher", "Martijn Lentink", "Haalt links uit POP3-emails", "1.0", "nl")]
    [ScriptInfo("POP3 EmailFetcher", "Martijn Lentink", "Gets the urls by POP3-email", "1.0", "en")]
    public sealed class EmailFetcher : UriCollector, IDisposable
    {
        private readonly string settingsfile = Properties.Settings.Default.SettingsFolder + @"PMC.UriCollector.EmailFetcher.xml";
        private readonly Pop3Client pop3 = new Pop3Client();

        private EmailFetcherSettings settings;

        public override void Update()
        {
            if (Login())
            {
                Process();
                Disconnect();
            }
        }

        public override Task UpdateAsync()
        {
            return Task.Factory.StartNew(Update);
        }

        public override DialogResult ShowSettings()
        {
            return Settings(true);
        }

        private bool changed;
        public override bool Changed
        {
            get { return changed; }
            protected set { changed = value; }
        }

        public override void LoadSettings()
        {
            if (File.Exists(settingsfile))
            {
                try
                {
                    settings = Global.XMLDeserialize<EmailFetcherSettings>(settingsfile);
                    //changed = false;
                    return;
                }
                catch
                {
                    //Give message that settings could not load.
                }
            }
            settings = new EmailFetcherSettings();
            //BUG Settings(false); //If loading was not succesfull or no settings found
        }

        public override void SaveSettings()
        {
            if (!Equals(settings, default(EmailFetcherSettings)))
            {
                try
                {
                    Log.Instance.AddMessage("Saving settings.");
                    Global.XMLSerialize(settingsfile, settings);
                    changed = false;
                }
                catch (Exception e)
                {
                    Log.Instance.AddException(e);
                    throw;
                }
            }
        }

        public override void Reload()
        {
            Load();
            changed = true;
        }

        #region Settings
        private DialogResult Settings(bool allowCancel)
        {
            using (
                var settingsForm = new EmailFetcherSettingForm(allowCancel)
                    {
                        Username = this.settings.Username,
                        Password = this.settings.Password,
                        Host = this.settings.Host,
                        Port = this.settings.Port,
                        SSL = this.settings.Ssl
                    })
            {
                settingsForm.ShowDialog();
                if (settingsForm.DialogResult == DialogResult.OK)
                {
                    this.settings.Username = settingsForm.Username;
                    this.settings.Password = settingsForm.Password;
                    this.settings.Host = settingsForm.Host;
                    this.settings.Port = settingsForm.Port;
                    this.settings.Ssl = settingsForm.SSL;
                    changed = true;
                }
                return settingsForm.DialogResult;
            }
        }


        #endregion

        #region Connection

        private bool Login()
        {
            try
            {
                if (!Global.CheckInternetConnection())
                    throw new ConnectionException();
                Log.Instance.AddMessage("Logging in to: " + settings.Host + " with user " + settings.Username);
                if (!pop3.Connected)
                    pop3.Connect(settings.Host, settings.Port, settings.Ssl);
                pop3.Authenticate(settings.Username, settings.Password);
                return true;
            }
            catch (Exception e)
            {
                Log.Instance.AddException(e);
                OnError(e);
                Disconnect();
                return false;
            }
        }

        private void Disconnect()
        {
            try
            {
                pop3.Disconnect();
            }
            catch (Exception e)
            {
                Log.Instance.AddException(e);
            }
        }

        #endregion

        #region Process Email

        private void Process()
        {
            try
            {
                int count = pop3.GetMessageCount();
                Log.Instance.AddMessage("Number of e-mails: " + count);

                for (int i = count; i >= 1; i--)
                {
                    OpenPop.Mime.Message message;
                    try
                    {
                        message = pop3.GetMessage(i);
                    }
                    catch (Exception e)
                    {
                        Log.Instance.AddMessage(e);
                        continue;
                    }

                    Log.Instance.AddMessage("Opening mail: " + message.Headers.Subject);
                    var m = Encoding.ASCII.GetString(message.RawMessage);
                    var document = Global.HtmlUtility.StringToHtmlDocument(m);

                    foreach (var link in document.DocumentNode.Descendants("a"))
                    {
                        string url;
                        try
                        {
                            url = link.GetAttributeValue("href", string.Empty);
                        }
                        catch (UnauthorizedAccessException exception)
                        {
                            Log.Instance.AddMessage("Unable to get 'href'-attribute... " + exception, LogType.Warning);
                            continue;
                        }
                        if (url.IndexOf("http", StringComparison.OrdinalIgnoreCase) == -1 && !Regex.IsMatch(url, @"(http|https)://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?"))
                            continue;

                        Uri href;
                        try
                        {
                            if (!Uri.TryCreate(url, UriKind.Absolute, out href))
                                continue;
                        }
                        catch (UriFormatException) { continue; } //.NET may throw UriFormatException here

                        bool testString = UrlExpressions.Instance.TestString(href.ToString());

                        if (!testString)
                            continue;

                        Add(href);
                    }
                }
            }
            catch (Exception e)
            {
                Log.Instance.AddException(e);
                OnError(e);
            }
            Log.Instance.AddMessage("End of processing e-mails...");
            OnCompleted();
        }
        #endregion

        private void DisposeMe()
        {
            pop3.Dispose();
        }

        public void Dispose()
        {
            DisposeMe();
            GC.SuppressFinalize(this);
        }

        ~EmailFetcher()
        {
            DisposeMe();
        }
    }
}
